#include "CppUTest/TestHarness.h"

extern "C" {
	#include "board.h"
}

TEST_GROUP(BoardTest)
{
	void setup()
	{
		CreateNewBoard();
	}

	static void PlayerXWinningMovesNoCheck(void)
	{
		PlayerMove(1,PLAYER_X);
		PlayerMove(4,PLAYER_O);
		PlayerMove(2,PLAYER_X);
		PlayerMove(8,PLAYER_O);
		PlayerMove(3,PLAYER_X);
	}

	static void PlayerXWinningMoves(void)
	{
		PlayerXWinningMovesNoCheck();
		CheckForWin();
	}

	static void PlayerOWinningMovesNoCheck(void)
	{
		PlayerMove(1,PLAYER_X);
		PlayerMove(4,PLAYER_O);
		PlayerMove(7,PLAYER_X);
		PlayerMove(5,PLAYER_O);
		PlayerMove(3,PLAYER_X);
		PlayerMove(6,PLAYER_O);
	}

	static void PlayerOWinningMoves(void)
	{
		PlayerOWinningMovesNoCheck();
		CheckForWin();
	}

	void teardown()
	{
		DeleteBoard();
	}
};

TEST(BoardTest, BoardIsCorrectSize)
{
	LONGS_EQUAL(300, GetBoardSizeX());
	LONGS_EQUAL(300, GetBoardSizeY());
}

TEST(BoardTest, GetBoardSlot1FromCoords)
{
	LONGS_EQUAL(1, GetBoardSlotFromCoords(0,0));
	LONGS_EQUAL(1, GetBoardSlotFromCoords(99,0));
	LONGS_EQUAL(1, GetBoardSlotFromCoords(0,99));
	LONGS_EQUAL(1, GetBoardSlotFromCoords(99,99));
}

TEST(BoardTest, GetBoardSlot2FromCoords)
{
	LONGS_EQUAL(2, GetBoardSlotFromCoords(100,0));
	LONGS_EQUAL(2, GetBoardSlotFromCoords(199,0));
	LONGS_EQUAL(2, GetBoardSlotFromCoords(100,99));
	LONGS_EQUAL(2, GetBoardSlotFromCoords(199,99));
}

TEST(BoardTest, GetBoardSlot3FromCoords)
{
	LONGS_EQUAL(3, GetBoardSlotFromCoords(200,0));
	LONGS_EQUAL(3, GetBoardSlotFromCoords(299,0));
	LONGS_EQUAL(3, GetBoardSlotFromCoords(200,99));
	LONGS_EQUAL(3, GetBoardSlotFromCoords(299,99));
}

TEST(BoardTest, GetBoardSlot4FromCoords)
{
	LONGS_EQUAL(4, GetBoardSlotFromCoords(0,100));
	LONGS_EQUAL(4, GetBoardSlotFromCoords(99,100));
	LONGS_EQUAL(4, GetBoardSlotFromCoords(0,199));
	LONGS_EQUAL(4, GetBoardSlotFromCoords(99,199));
}

TEST(BoardTest, GetBoardSlot5FromCoords)
{
	LONGS_EQUAL(5, GetBoardSlotFromCoords(100,100));
	LONGS_EQUAL(5, GetBoardSlotFromCoords(199,100));
	LONGS_EQUAL(5, GetBoardSlotFromCoords(100,199));
	LONGS_EQUAL(5, GetBoardSlotFromCoords(199,199));
}

TEST(BoardTest, GetBoardSlot6FromCoords)
{
	LONGS_EQUAL(6, GetBoardSlotFromCoords(200,100));
	LONGS_EQUAL(6, GetBoardSlotFromCoords(299,100));
	LONGS_EQUAL(6, GetBoardSlotFromCoords(200,199));
	LONGS_EQUAL(6, GetBoardSlotFromCoords(299,199));
}

TEST(BoardTest, GetBoardSlot7FromCoords)
{
	LONGS_EQUAL(7, GetBoardSlotFromCoords(0,200));
	LONGS_EQUAL(7, GetBoardSlotFromCoords(99,200));
	LONGS_EQUAL(7, GetBoardSlotFromCoords(0,299));
	LONGS_EQUAL(7, GetBoardSlotFromCoords(99,299));
}

TEST(BoardTest, GetBoardSlot8FromCoords)
{
	LONGS_EQUAL(8, GetBoardSlotFromCoords(100,200));
	LONGS_EQUAL(8, GetBoardSlotFromCoords(199,200));
	LONGS_EQUAL(8, GetBoardSlotFromCoords(100,299));
	LONGS_EQUAL(8, GetBoardSlotFromCoords(199,299));
}

TEST(BoardTest, GetBoardSlot9FromCoords)
{
	LONGS_EQUAL(9, GetBoardSlotFromCoords(200,200));
	LONGS_EQUAL(9, GetBoardSlotFromCoords(299,200));
	LONGS_EQUAL(9, GetBoardSlotFromCoords(200,299));
	LONGS_EQUAL(9, GetBoardSlotFromCoords(299,299));
}

TEST(BoardTest, GetBoardSlot0FromInvalidCoords)
{
	LONGS_EQUAL(0, GetBoardSlotFromCoords(400,200)); /* X invalid */
	LONGS_EQUAL(0, GetBoardSlotFromCoords(200,400)); /* Y invalid */
	LONGS_EQUAL(0, GetBoardSlotFromCoords(400,400)); /* Both invalid */
}

TEST(BoardTest, AllSlotsUnoccupiedAtStart)
{
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(1));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(2));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(3));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(4));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(5));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(6));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(7));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(8));
	LONGS_EQUAL(UNOCCUPIED, GetOccupation(9));
}

TEST(BoardTest, SetAnUnoccupiedSlotToXIsAllowed)
{
	LONGS_EQUAL(1, SetOccupation(1,PLAYER_X));
}

TEST(BoardTest, SetAnUnoccupiedSlotToOIsAllowed)
{
	LONGS_EQUAL(1, SetOccupation(2,PLAYER_O));
}

TEST(BoardTest, SetAnOccupiedSlotToXIsNotAllowed)
{
	SetOccupation(1,PLAYER_X);
	LONGS_EQUAL(0, SetOccupation(1,PLAYER_X));
}

TEST(BoardTest, SetAnOccupiedSlotToOIsNotAllowed)
{
	SetOccupation(2,PLAYER_X);
	LONGS_EQUAL(0, SetOccupation(2,PLAYER_O));
}

TEST(BoardTest, PlayerXAlwaysGoesFirst)
{
	LONGS_EQUAL(PLAYER_X, GetCurrentPlayer());
}

TEST(BoardTest, PlayerODoesNotGoFirst)
{
	CHECK(PLAYER_O != GetCurrentPlayer());
}

TEST(BoardTest, PlayerOGoesAfterX)
{
	PlayerMove(7,PLAYER_X);
	LONGS_EQUAL(PLAYER_O, GetCurrentPlayer());
}

TEST(BoardTest, PlayerXGoesAfterO)
{
	PlayerMove(7,PLAYER_X);
	PlayerMove(6,PLAYER_O);
	LONGS_EQUAL(PLAYER_X, GetCurrentPlayer());
}

TEST(BoardTest, NoMoveWithSlot0)
{
	PlayerMove(0,PLAYER_X);
	LONGS_EQUAL(0, GetCurrentNumMoves());
	LONGS_EQUAL(PLAYER_X, GetCurrentPlayer());
}

TEST(BoardTest, MoveCountIncrementsCorrectly)
{
	LONGS_EQUAL(0, GetCurrentNumMoves());
	PlayerMove(2,PLAYER_X);
	LONGS_EQUAL(1, GetCurrentNumMoves());
	PlayerMove(1,PLAYER_O);
	LONGS_EQUAL(2, GetCurrentNumMoves());
	PlayerMove(5,PLAYER_X);
	LONGS_EQUAL(3, GetCurrentNumMoves());
	PlayerMove(8,PLAYER_O);
	LONGS_EQUAL(4, GetCurrentNumMoves());
	PlayerMove(7,PLAYER_X);
	LONGS_EQUAL(5, GetCurrentNumMoves());
	PlayerMove(3,PLAYER_O);
	LONGS_EQUAL(6, GetCurrentNumMoves());
	PlayerMove(6,PLAYER_X);
	LONGS_EQUAL(7, GetCurrentNumMoves());
	PlayerMove(4,PLAYER_O);
	LONGS_EQUAL(8, GetCurrentNumMoves());
	PlayerMove(9,PLAYER_X);
	LONGS_EQUAL(9, GetCurrentNumMoves());
}

TEST(BoardTest, BoardResetAfterDraw)
{
	PlayerMove(2,PLAYER_X);
	PlayerMove(1,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(8,PLAYER_O);
	PlayerMove(7,PLAYER_X);
	PlayerMove(3,PLAYER_O);
	PlayerMove(6,PLAYER_X);
	PlayerMove(4,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	LONGS_EQUAL(GAME_DRAW, CheckForWin());
	RestartGame();
	LONGS_EQUAL(IN_PROGRESS, CheckForWin());
	LONGS_EQUAL(0, GetCurrentNumMoves());
	LONGS_EQUAL(PLAYER_X, GetCurrentPlayer());
}

TEST(BoardTest, Test3OnTopRowIsDetected)
{
	/* Cells 1,2,3 */
	PlayerXWinningMovesNoCheck();
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnMiddleRowIsDetected)
{
	/* Cells 4,5,6 */
	PlayerMove(4,PLAYER_X);
	PlayerMove(2,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(8,PLAYER_O);
	PlayerMove(6,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnBottomRowIsDetected)
{
	/* Cells 7,8,9 */
	PlayerMove(7,PLAYER_X);
	PlayerMove(4,PLAYER_O);
	PlayerMove(8,PLAYER_X);
	PlayerMove(3,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnLeftColumnIsDetected)
{
	/* Cells 1,4,7 */
	PlayerMove(1,PLAYER_X);
	PlayerMove(9,PLAYER_O);
	PlayerMove(4,PLAYER_X);
	PlayerMove(6,PLAYER_O);
	PlayerMove(7,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnCentreColumnIsDetected)
{
	/* Cells 2,5,8 */
	PlayerMove(2,PLAYER_X);
	PlayerMove(3,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(9,PLAYER_O);
	PlayerMove(8,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnRightColumnIsDetected)
{
	/* Cells 3,6,9 */
	PlayerMove(3,PLAYER_X);
	PlayerMove(1,PLAYER_O);
	PlayerMove(6,PLAYER_X);
	PlayerMove(5,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnTopLeftToBottomRightDiagonalIsDetected)
{
	/* Cells 1,5,9 */
	PlayerMove(1,PLAYER_X);
	PlayerMove(6,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(2,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, Test3OnBottomLeftToTopRightDiagonalIsDetected)
{
	/* Cells 7,5,3 */
	PlayerMove(7,PLAYER_X);
	PlayerMove(4,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(8,PLAYER_O);
	PlayerMove(3,PLAYER_X);
	LONGS_EQUAL(GAME_WIN_X, CheckForWin());
}

TEST(BoardTest, TestOCanWin)
{
	/* Cells 7,5,3 */
	PlayerOWinningMovesNoCheck();
	LONGS_EQUAL(GAME_WIN_O, CheckForWin());
}

TEST(BoardTest, PlayerXTurnString)
{
	STRCMP_EQUAL("Player X's Turn", GetInfoString());
}

TEST(BoardTest, PlayerOTurnString)
{
	PlayerMove(1,PLAYER_X);
	STRCMP_EQUAL("Player O's Turn", GetInfoString());
}

TEST(BoardTest, PlayerXWinString)
{
	PlayerXWinningMoves();
	STRCMP_EQUAL("Player X Wins!", GetInfoString());
}

TEST(BoardTest, PlayerOWinString)
{
	PlayerOWinningMoves();
	STRCMP_EQUAL("Player O Wins!", GetInfoString());
}

TEST(BoardTest, CannotContinueGameAfterWin)
{
	PlayerXWinningMoves();
	LONGS_EQUAL(0, PlayerMove(9,PLAYER_O));
}

TEST(BoardTest, GameStateIsInProgressAtStart)
{
	LONGS_EQUAL(IN_PROGRESS, GetGameState());
}

TEST(BoardTest, GameStateIsDraw)
{
	PlayerMove(2,PLAYER_X);
	PlayerMove(1,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(8,PLAYER_O);
	PlayerMove(7,PLAYER_X);
	PlayerMove(3,PLAYER_O);
	PlayerMove(6,PLAYER_X);
	PlayerMove(4,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	CheckForWin();
	LONGS_EQUAL(GAME_DRAW, GetGameState());
}

TEST(BoardTest, GameStateIsXWin)
{
	PlayerXWinningMoves();
	LONGS_EQUAL(GAME_WIN_X, GetGameState());
}

TEST(BoardTest, GameStateIsOWin)
{
	PlayerOWinningMoves();
	LONGS_EQUAL(GAME_WIN_O, GetGameState());
}

TEST(BoardTest, NoWinsAtStartup)
{
	CheckForWin();
	LONGS_EQUAL(0, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());
}

TEST(BoardTest, GetXWin)
{
	PlayerXWinningMoves();
	LONGS_EQUAL(1, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());
}

TEST(BoardTest, GetOWin)
{
	PlayerOWinningMoves();
	LONGS_EQUAL(0, GetNumXWins());
	LONGS_EQUAL(1, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());
}

TEST(BoardTest, GetDraw)
{
	PlayerMove(2,PLAYER_X);
	PlayerMove(1,PLAYER_O);
	PlayerMove(5,PLAYER_X);
	PlayerMove(8,PLAYER_O);
	PlayerMove(7,PLAYER_X);
	PlayerMove(3,PLAYER_O);
	PlayerMove(6,PLAYER_X);
	PlayerMove(4,PLAYER_O);
	PlayerMove(9,PLAYER_X);
	CheckForWin();
	LONGS_EQUAL(0, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(1, GetNumDraws());
}

TEST(BoardTest, BothPlayersGetAWin)
{
	PlayerXWinningMoves();
	LONGS_EQUAL(1, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());

	RestartGame();

	PlayerOWinningMoves();
	LONGS_EQUAL(1, GetNumXWins());
	LONGS_EQUAL(1, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());
}

TEST(BoardTest, ClearStats)
{
	PlayerXWinningMoves();
	LONGS_EQUAL(1, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());

	ClearStats();

	LONGS_EQUAL(0, GetNumXWins());
	LONGS_EQUAL(0, GetNumOWins());
	LONGS_EQUAL(0, GetNumDraws());
}

TEST(BoardTest, AIIsInactiveAtStart)
{
	LONGS_EQUAL(0, GetAIActiveStatus());
}

TEST(BoardTest, AIActiveAfterToggle)
{
	LONGS_EQUAL(1, ToggleAIActive());
	LONGS_EQUAL(1, GetAIActiveStatus());
}

TEST(BoardTest, AIInactiveAfterDoubleToggle)
{
	LONGS_EQUAL(1, ToggleAIActive());
	LONGS_EQUAL(0, ToggleAIActive());
	LONGS_EQUAL(0, GetAIActiveStatus());
}

TEST(BoardTest, AI_NoXMovesAtAtart)
{
	LONGS_EQUAL(0, GetLastXMove());
}

TEST(BoardTest, AI_GetXMove)
{
	PlayerMove(2,PLAYER_X);
	LONGS_EQUAL(2, GetLastXMove());
}